Generalist Programmer and Technical Artist
The renderer is a deffered renderer, and runs on a seperate render thread, rendering the data supplied by the main game-thread of the previous frame.
Asset loading such as .obj, textures, my .shdr and .mat files uses multi-threading, and stores "runtime mesh" and "runtime material" in a sorted vector, for cache friendly access when generating render calls. The render thread iterates through the generated 'render requests' and 'runtime materials' to in a O(N + M) forloop and automatically instances meshes/materials that can be, and markted to be, instanced.
The runtime materials can be modified by systems/functions ingame, such as changing float values. The asset loading only supports .obj models.
To prevent the 'user/programmer' from making critical mistakes, I created a systems wrapper, which is an interface for the 'user/programmer' to be able to enqueue systems/functions that act on a set of components, without gaining access to core entt objects.
The systems wrapper allows to query for entities with specificed components as read or write, exclude entities with specified components, and mark components for other access and modification.
Systems can either be one a single thread, as "each", or an another thread as "enqueued each", or on multiple threads of either a set number of threads, or by chunk sizes, through "enqueue parallel each".
There's also a Systems context storage, which stores data in a thread-safe Systems context objects, in a map. This can then be accessed to create data that other systems may use at a later stage.
To complement the Systems Context Storage, there's also a "enqueue parallel data each", which allows for data as an input, aswell as entity iteration order. That way, the 'user/programmer' can read data in parallel, then launch parallel threads (with an enqueue for the worker pool) to process the data, to then iterate through those same entities to write the data back.
Since writing to components that are already being read or written to causes data race, the Systems Wrapper automatically registers and detects read/write collisions, and forces a thread syncronization before letting the system start.
There's an entity component creation/destruction buffer, which allows the user to create, or remove entities, and delete, or add components to existing or new entities, which is executed at a later synced state to avoid data races.
The editor itself is very rudimentary, but has some of the essential necessities. Such as 'prefabricates', duplication, parenting hierarchy, secene saving and loading, and component modification.
The drawing, saving and loading, aswell as in-engine creation adding components, is done fairly simply by the 'user/programmer', by adding components and using a macro to register them into necessary registers and factory.
A few examples of what the engine can do
125k cubes moving with a simple move script, all inviditually controlled.
5k rigid bodies, with a vibe coded physics solver (due to insufficient time in the course), with Spatial-partitoinig, AABB, OOBB, and collision manifold creation being multi-threaded, but the physics solver being single-threaded. The physics is due for adding features like gravity/attractors, and static rigid bodies, static/dynamic collision layers. Aswell as Landscape collisions and storing OBB collision data for entities or systems to access.